package org.grill.clovercash.storage;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;

import org.grill.clovercash.SplitComparator;
import org.grill.clovercash.accounts.Account;
import org.grill.clovercash.accounts.Split;
import org.grill.clovercash.accounts.SumRangeFinder;
import org.joda.time.LocalDate;

public class Reconciliation {
	private LocalDate date =  new LocalDate();
	private Account account;
//	private BigDecimal startAmount;
	private BigDecimal endAmount;
	
	private ArrayList<Split> splits = new ArrayList<Split>();
	private ArrayList<StatementEntry> statementEntries = new ArrayList<StatementEntry>();
	
	private boolean recurse = true;

	public Reconciliation(Account a) {
		this.account = a;
	}

	public Account getAccount() {
		return account;
	}

	public void setAccount(Account account) {
		this.account = account;
	}

	public LocalDate getDate() {
		return date;
	}

	public void setDate(LocalDate date) {
		this.date = date;
	}

	public BigDecimal getEndAmount() {
		return endAmount;
	}

	public void setEndAmount(BigDecimal endAmount) {
		this.endAmount = endAmount;
	}

	public boolean isRecurse() {
		return recurse;
	}

	public void setRecurse(boolean recurse) {
		this.recurse = recurse;
	}

	public ArrayList<Split> getSplits() {
		return splits;
	}

	public void setSplits(ArrayList<Split> splits) {
		this.splits = splits;
	}

//	public BigDecimal getStartAmount() {
//		return startAmount;
//	}

//	public void setStartAmount(BigDecimal startAmount) {
//		this.startAmount = startAmount;
//	}
	
	public BigDecimal getCurrentAmount() {
		BigDecimal reconciledTotal = account.getReconciledTotal(recurse);
		for (Split s : splits)
			reconciledTotal = reconciledTotal.add(s.value);
		return reconciledTotal;
	}
	
	public boolean canFinish() {
		return getCurrentAmount().equals(endAmount);
	}
	
	public BigDecimal getDifference() {
		return endAmount.subtract(getCurrentAmount());
	}

	private void fillSplits(Account a, ArrayList<Split> splits) {
		for (Split s : a.getSplits()) {
			if (!s.reconciled)
				splits.add(s);
		}
		
		if (recurse) {
			for (Account c : a.getChildren())
				fillSplits(c, splits);
		}
	}
	
	public ArrayList<Split> getUnreconciledSplits() {
		ArrayList<Split> splits = new ArrayList<Split>();
		fillSplits(account, splits);
		
		Collections.sort(splits, new SplitComparator());
		
		return splits;
	}

	public void addIntraAccountTransactions() {
		if (!recurse)
			return;
		
		for (Split s : getUnreconciledSplits()) 
			if (account.isInHierarchy(s.trans.getOther(s).owner))
				splits.add(s);
	}
	
	public boolean isInHierarchy(Account a) {
		if (!recurse) 
			return a == account;
		return account.isInHierarchy(a);
	}

	public void setStatementEntries(ArrayList<StatementEntry> statementEntries) {
		this.statementEntries = statementEntries;
	}
	
	public ArrayList<StatementEntry> getStatementEntries() {
		return statementEntries;
	}
	
	public void checkSplitsAreStillValid() {
		for (Split s : splits.toArray(new Split[0])) {
			if (recurse) {
				if (!account.isInHierarchy(s.owner))
					splits.remove(s);
			} else {
				if (account != s.owner)
					splits.remove(s);
			}
		}
	}
	

	public ArrayList<StatementEntry> getMatchingStatementEntries() {
		ArrayList<StatementEntry> matchingEntries = new ArrayList<StatementEntry>();
		
		BigDecimal[] vals = new BigDecimal[statementEntries.size()];
		for (int i = 0; i < vals.length; i++)
			vals[i] = statementEntries.get(i).value;
		
		int[] idxs = new SumRangeFinder().find(getEndAmount().subtract(getCurrentAmount()), vals);
		if (idxs[0] != -1) {
			for (int i = idxs[0]; i <= idxs[1]; i++)
				matchingEntries.add(statementEntries.get(i));
		}
		return matchingEntries;
	}
}
